1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.util.data_structures;
12 
13 public import hip.util.string: String;
14 struct Pair(A, B, string aliasA = "", string aliasB = "")
15 {
16 
17     A first;
18     B second;
19 
20     alias a = first;
21     alias b = second;
22 
23     static if(aliasA != "")
24         mixin("alias "~aliasA~" = first;");
25     static if(aliasB != "")
26         mixin("alias "~aliasB~" = second;");
27 }
28 struct Dirty(T)
29 {
30     T value;
31     private bool isDirty;
32 
33     auto opAssign(T value)
34     {
35         if(value != this.value) this.value = value, isDirty = true;
36         return this;
37     }
38     void clearDirtyFlag(){isDirty = false;}
39 }
40 
41 mixin template DirtyFlagFields(string flagName, T, string[] fields)
42 {
43     static foreach(f; fields)
44     {
45         mixin("T _",f,";",
46         "T ",f,"() => _",f,";",
47         "T ",f,"(T v){if(_",f," != v) ",flagName," = true; return _",f," = v;}");
48     }
49 }
50 
51 struct DirtyFlagsCmp(alias flag, Fields...)
52 {
53     import std.typecons;
54     static if(is(__traits(parent, Fields[0]) == struct))
55     	private static alias P = __traits(parent, Fields[0])*;
56    	else
57     	private static alias P = __traits(parent, Fields[0]);
58 	P parent;
59     Tuple!(typeof(Fields)) base;
60 
61     this(P parent)
62     {
63         start(parent);
64     }
65 
66     void start(P parent)
67     {
68         this.parent = parent;
69         static foreach (i, f; Fields)
70             base[i] = __traits(child, parent, f);
71     }
72 
73     void update()
74     {
75         bool changed;
76         static foreach (i, f; Fields)
77         {
78             {
79                 alias T = typeof(f);
80                 changed = changed || (__traits(child, parent, f) !is base[i]);
81             }
82         }
83         __traits(child, parent, flag) = __traits(child, parent, flag) || changed;
84     }
85     alias opCall = update;
86 }
87 
88 
89 
90 /** 
91  * RangeMap allows specifying a range in which a value spams, quite useful for defining outcomes
92  *  based on an input, experience gain progression, etc. Example Usage:
93  *  ```d
94  *  RangeMap!(int, string) colorRanges = "White"; //Default is "White"
95  *  colorRanges[0..9] = "Red";
96  *  colorRanges[10..19] = "Green";
97  *  colorRanges[20..29] = "Blue"
98  *  
99  *  writeln(colorRanges[5]); //Prints "Red"
100  *  ```
101  */
102 struct RangeMap(K, V)
103 {
104     import std.traits:isNumeric;
105     @nogc:
106     static assert(isNumeric!K, "RangeMap key must be a numeric type");
107     protected Array!K ranges;
108     protected Array!V values;
109     protected V _default;
110 
111     /**
112     *   When the value is out of range, the value returned is the _default one.
113     */
114     ref RangeMap setDefault(V _default)
115     {
116         this._default = _default;
117         return this;
118     }
119     /** 
120      * Alternative to the slice syntax
121      */
122     ref RangeMap setRange(K a, K b, V value)
123     {
124         if(ranges == null)
125         {
126             ranges = Array!K(8);
127             values = Array!V(8);
128         }
129         int rLength = cast(int)ranges.length;
130         ranges.reserve(ranges.length+2);
131         if(a > b)
132         {
133             K temp = a;
134             a = b;
135             b = temp;
136         }
137         if(rLength != 0 && ranges[rLength-1] > a) //Silently ignore for now
138             return this;
139         ranges~=a;
140         ranges~=b;
141         values~=value;
142         return this;
143     }
144 
145     /**
146     *   Uses binary search for finding the value range.
147     */
148     V get(K value)
149     {
150         int l = 0;
151         int length = cast(int)ranges.length;
152         int r = length-1;
153 
154         while(l < r)
155         {
156             int m = cast(int)((l+r)/2);
157             if(m % 2 != 0)
158                 m--;
159             K k = ranges[m];
160             //Check if value is between a[m] and a[m-1]
161             if(m+1 < length && value >= k && value <= ranges[m+1])
162                 return values[cast(int)(m/2)];
163             else if(k < value)
164                 l = m + 1;
165             else if(m > value)
166                 r = m - 1;
167             //Check if both values on the right is greater than value
168             else if(m+1 < length && k > value && ranges[m+1] > value)
169                 break;
170         }
171         return _default;
172     }
173 
174     pragma(inline) auto ref opAssign(V value)
175     {
176         setDefault(value);
177         return this;
178     }
179     pragma(inline) V opSliceAssign(V value, K start, K end)
180     {
181         setRange(start, end, value);
182         return value;
183     }
184     pragma(inline) V opIndex(K index){return get(index);}
185 }
186 /**
187 *   RefCounted, Array @nogc, OutputRange compatible, it aims to bring the same result as one would have by using
188 *   int[], Array!int should be equivalent, any different behaviour should be contacted. 
189 *   It may use more memory than requested for not making reallocation a big bottleneck
190 */
191 struct Array(T) 
192 {
193     size_t length;
194     T* data;
195     private size_t capacity;
196     private int* countPtr;
197     import core.stdc.stdlib:malloc;
198     import core.stdc.string:memcpy, memset;
199     import core.stdc.stdlib:realloc;
200 
201     this(this) @nogc
202     {
203         *countPtr = *countPtr + 1;
204     }
205     alias _opApplyFn = int delegate(ref T) @nogc;
206     pragma(inline) int opApply(scope _opApplyFn dg) @nogc
207     {
208         int result = 0;
209         for(int i = 0; i < length && result; i++)
210             result = dg(data[i]);
211         return result;
212     }
213     private void initialize(size_t capacity) @nogc
214     {
215         this.data = cast(T*)malloc(T.sizeof*capacity);
216         this.capacity = capacity;
217         this.countPtr = cast(int*)malloc(int.sizeof);
218         *this.countPtr = 1;
219         this[0..capacity] = T.init;
220     }
221 
222     static Array!T opCall(size_t capacity = 1) @nogc
223     {
224         Array!T ret;
225         ret.initialize(capacity);
226         return ret;
227     }
228 
229     static Array!T opCall(size_t length, T value) @nogc
230     {
231         Array!T ret = Array!(T)(length);
232         ret.length = length;
233         ret[] = value;
234         return ret;
235     }
236     
237     static Array!T opCall(scope T[] arr) @nogc
238     {
239         Array!T ret = Array!(T)(arr.length);
240         ret.length = arr.length;
241         memcpy(ret.data, arr.ptr, ret.length*T.sizeof);
242         return ret;
243     }
244     void* getRef()
245     {
246         *countPtr = *countPtr + 1;
247         return cast(void*)&this;
248     }
249 
250     pragma(inline) bool opEquals(R)(const R other) const
251     if(is(R == typeof(null)))
252     {
253         return data == null;
254     }
255     void dispose() @nogc
256     {
257         import core.stdc.stdlib:free;
258         if(data != null)
259         {
260             free(data);
261             free(countPtr);
262             data = null;
263             countPtr = null;
264             length = capacity = 0;
265         }
266     }
267     immutable(T*) ptr(){return cast(immutable(T*))data;}
268     size_t opDollar() @nogc {return length;}
269 
270     T[] opSlice() @nogc
271     {
272         return data[0..length];
273     }
274 
275     T[] opSlice(size_t start, size_t end) @nogc
276     {
277         return data[start..end];
278     }
279     auto ref opSliceAssign(T)(T value) @nogc
280     {
281         data[0..length] = value;
282         return this;
283     }
284     auto ref opSliceAssign(T)(T value, size_t start, size_t end) @nogc
285     {
286         data[start..end] = value;
287         return this;
288     }
289     import std.traits:isArray, isNumeric;
290     auto ref opAssign(Q)(Q value) @nogc
291     if(isArray!Q)
292     {
293         if(data == null)
294             data = cast(T*)malloc(T.sizeof*value.length);
295         else
296             data = cast(T*)realloc(data, T.sizeof*value.length);
297         length = value.length;
298         capacity = value.length;
299         memcpy(data, value.ptr, T.sizeof*value.length);
300         return this;
301     }
302 
303     ref auto opIndex(size_t index) @nogc
304     {
305         assert(index>= 0 && index < length, "Array out of bounds");
306         return data[index];
307     }
308 
309     pragma(inline) private void resize(uint newSize) @nogc
310     {
311         if(data == null || capacity == 0)
312             initialize(newSize);
313         else
314         {
315             data = cast(T*)realloc(data, newSize*T.sizeof);
316             capacity = newSize;
317         }
318     }
319     ///Guarantee that no allocation will occur for the specified reserve amount of memory
320     void reserve(size_t newSize){if(newSize > capacity)resize(cast(uint)newSize);}
321     auto ref opOpAssign(string op, Q)(Q value) @nogc if(op == "~")
322     {
323         if(*countPtr != 1)
324         {
325             //Save old data
326             T* oldData = data;
327             //Remove from the ref counter
328             *countPtr = *countPtr - 1;
329             //Re initialize
330             initialize(length);
331             memcpy(data, oldData, T.sizeof*length);
332         }
333         static if(is(Q == T))
334         {
335             if(data == null)
336                 initialize(1);
337             if(length + 1 >= capacity)
338                 resize(cast(uint)((length+1)*1.5));
339             data[length++] = value;
340         }
341         else static if(is(Q == T[]) || is(Q == Array!T))
342         {
343             if(data == null)
344                 initialize(value.length);
345             if(length + value.length >= capacity)
346                 resize(cast(uint)((length+value.length)*1.5));
347             memcpy(data+length, value.ptr, T.sizeof*value.length);
348             length+= value.length;    
349         }
350         return this;
351     }
352 
353     String toString() @nogc
354     {
355         return String(this[0..$]);
356     }
357     void put(T data) @nogc {this~= data;}
358     ~this() @nogc
359     {
360         if(countPtr != null)
361         {
362             *countPtr = *countPtr - 1;
363             if(*countPtr <= 0)
364                 dispose();
365             countPtr = null;
366         }
367     }
368 }
369 
370 
371 private mixin template Array2DMixin(T)
372 {
373     int opApply(scope int delegate(ref T) dg)
374     {
375         int result = 0;
376         for(int i = 0; i < width*height; i++)
377         {
378             result = dg(data[i]);
379             if (result)
380                 break;
381         }
382         return result;
383     }
384 
385     private uint height;
386     private uint width;
387     @nogc int getWidth() const {return width;}
388     @nogc int getHeight() const {return height;}
389     @nogc T[] opSlice(size_t start, size_t end)
390     {
391         if(end < start)
392         {
393             size_t temp = end;
394             end = start; end = temp;
395         }
396         if(end > width*height)
397             end = width*height;
398         return data[start..end];
399     }
400     pragma(inline, true)
401     {
402         @nogc ref auto opIndex(size_t i,  size_t j){return data[i*width+j];}
403         @nogc ref auto opIndex(size_t i)
404         {
405             size_t temp = i*width;
406             return data[temp..temp+width];
407         }
408         @nogc auto opIndexAssign(T)(T value, size_t i, size_t j){return data[i*width+j] = value;}
409         @nogc size_t opDollar() const {return width*height;}
410         @nogc bool opCast() const{return data !is null;}
411     }
412 }
413 
414 /**
415 *   By using Array2D instead of T[][], only one array instance is created, not "n" arrays. So it is a lot
416 *   faster when you use that instead of array of arrays.
417 *   
418 *   Its main usage is for avoiding a[i][j] static array, and not needing to deal with array of arrays.
419 */
420 struct Array2D(T)
421 {
422 
423     mixin Array2DMixin!T;
424     Array2D_GC!T toGC()
425     {
426         Array2D_GC!T ret = new Array2D_GC!T(width, height);
427         ret.data[0..width*height] = data[0..width*height];
428         destroy(this);
429 
430         return ret;
431     }
432     @nogc:
433     private T* data;
434     import core.stdc.stdlib;
435 
436     private int* countPtr;
437 
438     this(this){*countPtr = *countPtr + 1;}
439     this(uint lengthHeight, uint lengthWidth)
440     {
441         data = cast(T*)malloc((lengthHeight*lengthWidth)*T.sizeof);
442         countPtr = cast(int*)malloc(int.sizeof);
443         *countPtr = 0;
444         height = lengthHeight;
445         width = lengthWidth;
446     }
447     ~this()
448     {
449         if(countPtr == null)
450             return;
451         *countPtr = *countPtr - 1;
452         if(*countPtr <= 0)
453         {
454             free(data);
455             free(countPtr);
456             data = null;
457             countPtr = null;
458             width = height = 0;
459         }
460     }
461     
462 
463 }
464 
465 class Array2D_GC(T)
466 {
467     private T[] data;
468     this(uint lengthHeight, uint lengthWidth)
469     {
470         data = new T[](lengthHeight*lengthWidth);
471         width = lengthWidth;
472         height = lengthHeight;
473     }
474     mixin Array2DMixin!T;
475 }
476 
477 private uint hash_fnv1(T)(T value)
478 {
479     import std.traits:isArray, isPointer;
480     enum fnv_offset_basis = 0x811c9dc5;
481     enum fnv_prime = 0x01000193;
482 
483     byte[] data;
484     static if(isArray!T)
485         data = (cast(byte*)value.ptr)[0..value.length*T.sizeof];
486     else static if(is(T == interface) || is(T == class) || isPointer!T)
487         data = cast(byte[])(cast(void*)value)[0..(void*).sizeof];
488     else //Value types: int, float, struct, etc
489         data = (cast(byte*)&value)[0..T.sizeof];
490 
491     typeof(return) hash = fnv_offset_basis;
492 
493     foreach(byteFromData; data)
494     {
495         hash*= fnv_prime;
496         hash^= byteFromData;
497     }
498     return hash;
499 }
500 
501 struct Map(K, V)
502 {
503     import core.stdc.stdlib;
504     static enum initializationLength = 128;
505     private static enum increasingFactor = 1.5;
506     private static enum increasingThreshold = 0.7;
507     private alias hashFunc = hash_fnv1;
508 
509     private struct MapData
510     {
511         K key;
512         V value;
513     }
514     private struct MapBucket
515     {
516         MapData data;
517         MapBucket* next;
518     }
519     private Array!MapBucket dataSet;
520 
521     private int* countPtr;
522 
523     this(this)
524     {
525         if(countPtr !is null)
526             *countPtr = *countPtr + 1;
527     }
528 
529     private int entriesCount;
530 
531     private void initialize() @nogc
532     {
533         dataSet = Array!(MapBucket)(initializationLength);
534         dataSet.length = initializationLength;
535         dataSet[] = MapBucket.init;
536         countPtr = cast(int*)malloc(int.sizeof);
537         *countPtr = 0;
538     }
539     static auto opCall() @nogc
540     {
541         Map!(K, V) ret;
542         ret.initialize();
543         return ret;
544     }
545 
546 
547 
548     void clear() @nogc 
549     {
550         entriesCount = 0;
551         for(int i = 0; i < dataSet.length; i++)
552         {
553             if(dataSet[i] != MapBucket.init)
554             {
555                 MapBucket* buck = dataSet[i].next;
556                 while(buck != null)
557                 {
558                     MapBucket copy = *buck;
559                     free(buck);
560                     buck = copy.next;
561                 }
562             }
563         }
564     }
565     //Called when array is filled at least increasingThreshold
566     private void recalculateHashes() @nogc
567     {
568         size_t newSize = cast(size_t)(dataSet.capacity * increasingFactor);
569         Array!MapBucket newArray = Array!MapBucket(newSize);
570 
571         for(int i = 0; i < dataSet.length; i++)
572         {
573             if(dataSet[i] != MapBucket.init)
574             {
575                 newArray[hashFunc(dataSet[i].data.key) % newSize] = dataSet[i];
576             }
577         }
578 
579         dataSet = newArray;
580     }
581     
582     bool has(K key) @nogc {return dataSet[getIndex(key)] != MapBucket.init;}
583     pragma(inline) uint getIndex(K key) @nogc {return hashFunc(key) % dataSet.length;}
584 
585 
586     ref auto opIndex(K index) @nogc
587     {
588         if(dataSet.length == 0)
589             initialize();
590         return dataSet[getIndex(index)].data.value;
591     }
592     ref auto opIndexAssign(V value, K key) @nogc
593     {
594         if(dataSet.length == 0)
595             initialize();
596         uint i = getIndex(key);
597         //Unexisting bucket
598         if(dataSet[i] == MapBucket.init)
599         {
600             entriesCount++;
601             dataSet[i] = MapBucket(MapData(key, value), null);
602             if(entriesCount > dataSet.length * increasingThreshold)
603                 recalculateHashes();
604         }
605         else //Existing bucket
606         {
607             MapBucket* curr = &dataSet[i];
608             do
609             {
610                 //Check if the key is the same as the other.
611                 if(curr.data.key == key)
612                 {
613                     curr.data.value = value;
614                     return this;
615                 }
616                 else if(curr.next != null)
617                     curr = curr.next;
618             }
619             while(curr.next != null);
620             curr.next = cast(MapBucket*)malloc(MapBucket.sizeof);
621             *curr.next = MapBucket(MapData(key, value), null);
622 
623         }
624         return this;
625     }
626 
627     auto opBinaryRight(string op, L)(const L lhs) const @nogc
628     if(op == "in")
629     {
630         if(dataSet.length == 0)
631             initialize();
632         uint i = getIndex(key);
633         if(dataSet[i] == MapBucket.init)
634             return null;
635         return &dataSet[i];
636     }
637 
638     ~this() @nogc
639     {
640         if(countPtr !is null)
641         {
642             *countPtr = *countPtr - 1;
643             if(*countPtr == 0)
644             {
645                 //Dispose
646                 clear();
647                 free(countPtr);
648             }
649             countPtr = null;
650         }
651     }
652 
653 }
654 alias AArray = Map;
655 
656 struct RingBuffer(T, uint Length)
657 {
658     import hip.util.concurrency:Volatile;
659     @nogc:
660 
661     T[Length] data;
662     private Volatile!uint writeCursor;
663     private Volatile!uint readCursor;
664 
665     this()
666     {
667         this.writeCursor = 0;
668         this.readCursor = 0;
669     }
670 
671     void push(T data)
672     {
673         this.data[writeCursor] = data;
674         writeCursor = (writeCursor+1) % Length;
675     }
676     ///It may read less than count if it is out of bounds
677     immutable T[] read(uint count)
678     {
679         uint temp = readCursor;
680         if(temp + count > Length)
681         {
682             readCursor = 0;
683             return data[temp..Length];
684         }
685         readCursor = (temp+count)%Length;
686         return data[temp .. count];
687     }
688 
689     immutable T read()
690     {
691         uint temp = readCursor;
692         immutable T ret = data[temp];
693         readCursor = (temp+1)%Length;
694         return ret;
695     }
696 
697     void dispose()
698     {
699         data = null;
700         length = 0;
701         writeCursor = 0;
702         readCursor = 0;
703     }
704 
705     ~this()
706     {
707         dispose();
708     }
709 }
710 
711 /**
712 *   High efficient(at least memory-wise), tightly packed Input queue that supports any kind of data in
713 *   a single allocated memory pool(no fragmentation).
714 *
715 *   This class could probably be extendeded to be every event handler
716 */
717 class EventQueue
718 {
719     import hip.util.memory;
720     @nogc:
721     
722     struct Event
723     {
724         ubyte type;
725         ubyte evSize;
726         void[0] evData;
727     }
728 
729     ///Linearly allocated variable length Events
730     void* eventQueue;
731     ///BytesOffset should never be greater than capacity
732     uint bytesCapacity;
733     uint bytesOffset;
734     protected uint pollCursor;
735 
736     protected this(uint capacity)
737     {
738         ///Uses capacity*greatest structure size
739         bytesCapacity = cast(uint)capacity;
740         bytesOffset = 0;
741         eventQueue = malloc(bytesCapacity);
742     }
743 
744 
745     void post(T)(ubyte type, T ev)
746     {
747         assert(bytesOffset+T.sizeof+Event.sizeof < bytesCapacity, "InputQueue Out of bounds");
748         if(pollCursor == bytesOffset) //It will restart if everything was polled
749         {
750             pollCursor = 0;
751             bytesOffset = 0;
752         }
753         else if(pollCursor != 0) //It will copy everything from the right to the start
754         {
755             memcpy(eventQueue, eventQueue+pollCursor, bytesOffset-pollCursor);
756             //Compensates the offset into the cursor.
757             bytesOffset-= pollCursor;
758             //Restarts the poll cursor, as everything from the right moved to left
759             pollCursor = 0;
760         }
761         Event temp;
762         temp.type = type;
763         temp.evSize = T.sizeof;
764         memcpy(eventQueue+bytesOffset, &temp, Event.sizeof);
765         memcpy(eventQueue+bytesOffset+Event.sizeof, &ev, T.sizeof);
766         bytesOffset+= T.sizeof+Event.sizeof;
767     }
768 
769     void clear()
770     {
771         //By setting it equal, no one will be able to poll it
772         pollCursor = bytesOffset;
773     }
774 
775     Event* poll()
776     {
777         if(bytesOffset - pollCursor <= 0)
778             return null;
779         Event* ev = cast(Event*)(eventQueue+pollCursor);
780         pollCursor+= ev.evSize+Event.sizeof;
781         return ev;
782     }
783 
784     protected ~this()
785     {
786         free(eventQueue);
787         eventQueue = null;
788         pollCursor = 0;
789         bytesCapacity = 0;
790         bytesOffset = 0;
791     }
792 }
793 
794 class Node(T)
795 {
796     T data;
797     Node!(T)[] children;
798     Node!T parent;
799 
800     this(T data){this.data = data;}
801 
802     pragma(inline) bool isRoot() const @nogc nothrow {return parent is null;}
803     pragma(inline) bool hasChildren() const @nogc nothrow {return children.length != 0;}
804     Node!T get(T data)
805     {
806         foreach(node; this)
807         {
808             if(node.data == data)
809                 return node;
810         }
811         return null;
812     }
813     pragma(inline) Node!T addChild(T data)
814     {
815         Node!T ret = new Node(data);
816         ret.parent = this;
817         children~= ret;
818         return ret;
819     }
820 
821     Node!T getRoot()
822     {
823         Node!T ret = this;
824         while(ret.parent !is null)
825             ret = ret.parent;
826         return ret;
827     }
828     
829     int opApply(scope int delegate(Node!T) cb)
830     {
831         foreach(child; children)
832         {
833             if(cb(child) || child.opApply(cb))
834                 return 1;
835         }
836         return 0;
837     }
838 }
839 
840 struct Signal(A...)
841 {
842     Array!(void function(A)) listeners;
843     void dispatch(A a)
844     {
845         foreach (void function(A) l; listeners)
846             l(a);
847     }
848 }